package com.one.mycommons.utils;

import java.sql.Connection;
import java.sql.Connection;  
import java.sql.PreparedStatement;  
import java.sql.ResultSet;  
import java.util.Properties;  
  
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.mapping.BoundSql;  
import org.apache.ibatis.mapping.MappedStatement;  
import org.apache.ibatis.mapping.MappedStatement.Builder;  
import org.apache.ibatis.mapping.ParameterMapping;  
import org.apache.ibatis.mapping.SqlSource;  
import org.apache.ibatis.plugin.Interceptor;  
import org.apache.ibatis.plugin.Intercepts;  
import org.apache.ibatis.plugin.Invocation;  
import org.apache.ibatis.plugin.Plugin;  
import org.apache.ibatis.plugin.Signature;  
import org.apache.ibatis.session.ResultHandler;  
import org.apache.ibatis.session.RowBounds;  
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.apache.ibatis.scripting.defaults.DefaultParameterHandler;

@Intercepts({
    @Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class}),  
    @Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class})
})  
public class PageInterceptor implements Interceptor {
	
	/** 存储所有语句名称 */
 /*   HashMap<String, String> map_statement = new HashMap<String, String>();*/
  
    /** 用户提供分页计算条数后缀 */
    private static final String SELECT_ID="page";
 
    //插件运行的代码，它将代替原有的方法
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        
        if (invocation.getTarget() instanceof StatementHandler) {  
            StatementHandler statementHandler = (StatementHandler) invocation.getTarget();  
            MetaObject metaStatementHandler = SystemMetaObject.forObject(statementHandler);  
            MappedStatement mappedStatement=(MappedStatement) metaStatementHandler.getValue("delegate.mappedStatement");
            String selectId=mappedStatement.getId();
//            System.out.println("  ----------  selectId   ----   :  " + selectId);
            if(selectId.substring(selectId.lastIndexOf(".")+1).toLowerCase().contains(SELECT_ID)){
                BoundSql boundSql = (BoundSql) metaStatementHandler.getValue("delegate.boundSql");  
                // 分页参数作为参数对象parameterObject的一个属性  
                String sql = boundSql.getSql();
                Page co=(Page)(boundSql.getParameterObject());
                if (null == co) {
					co = new Page();
				}
                
                // 重写 Sql语句  
                String countSql=concatCountSql(sql);
                String pageSql=concatPageMySql(sql,co);
//                
//                System.out.println("重写的 count  sql        :"+countSql);
//                System.out.println("重写的 select sql        :"+pageSql);
                Connection connection = (Connection) invocation.getArgs()[0];  
                
                ResultSet rs = null;  
                int totalCount = 0;  
                try { 
                
                	PreparedStatement  countStmt = connection.prepareStatement(countSql);  
                      BoundSql countBS = new BoundSql(mappedStatement.getConfiguration(), countSql,  
                              boundSql.getParameterMappings(), boundSql.getParameterObject());  
                      setParameters(countStmt, mappedStatement, countBS, boundSql.getParameterObject());  
                	
//                	
//                	PreparedStatement countStmt = connection.prepareStatement(countSql);    
//                    BoundSql countBS = copyFromBoundSql(mappedStatement, boundSql, countSql);  
//                    DefaultParameterHandler parameterHandler = new DefaultParameterHandler(mappedStatement, parameterObject, countBS);  
//                    parameterHandler.setParameters(countStmt);  
//                    countStmt = connection.prepareStatement(countSql);  
                    rs = countStmt.executeQuery();  
                    if (rs.next()) {  
                        totalCount = rs.getInt(1);  
                    } 
                    
                } catch (SQLException e) {  
//                    System.out.println("Ignore this exception"+e);  
                } finally {  
                    try {  
                        rs.close();  
                    } catch (SQLException e) {  
//                        System.out.println("Ignore this exception"+ e);  
                    }  
                }  
                
                metaStatementHandler.setValue("delegate.boundSql.sql", pageSql);            
                //绑定count
                co.setTotle(totalCount);
            }
        } 
        
        return invocation.proceed();
    }
    
    /**  
     * <一句话功能简述> 重写sql
     * <功能详细描述>
     * @param sql
     * @return
     * @see [类、类#方法、类#成员]
     */
    public String concatCountSql(String sql){
        StringBuffer sb=new StringBuffer("select count(*) from ");
        sql=sql.toLowerCase();
        
        if(sql.lastIndexOf("order")>sql.lastIndexOf(")")){
            sb.append(sql.substring(sql.indexOf("from")+4, sql.lastIndexOf("order")));
        }else{
            sb.append(sql.substring(sql.indexOf("from")+4));
        }
        return sb.toString();
    }
    
    /**
     * <一句话功能简述> 重写sql
     * <功能详细描述>
     * @param sql
     * @param page
     * @return
     * @see [类、类#方法、类#成员]
     */
    public String concatPageMySql(String sql,Page page){
        StringBuffer sb=new StringBuffer();
        sb.append(sql);
        int size = page.getPageSize();
        int index = page.getIndex();
        if (index > 0) {
        	sb.append(" limit ").append(size*index).append(" , ").append(size);
		}
        else {
        	sb.append(" limit ").append(0).append(" , ").append(size);
		}
        return sb.toString();
    }
    
    /**
	 * 拦截类型StatementHandler 
	 */
	@Override
	public Object plugin(Object target) {
	    if (target instanceof StatementHandler) {  
	        return Plugin.wrap(target, this);  
	    } else {  
	        return target;  
	    }  
	}

	@Override
	public void setProperties(Properties properties) {
	    
	}

	public void setPageCount(){
        
    }
	
	/** 
	 * 对SQL参数(?)设值 
	 *  
	 * @param ps 
	 * @param mappedStatement 
	 * @param boundSql 
	 * @param parameterObject 
	 * @throws SQLException 
	 */  
	private void setParameters(PreparedStatement ps, MappedStatement mappedStatement, BoundSql boundSql,  
	        Object parameterObject) throws SQLException {  
	    ParameterHandler parameterHandler = new DefaultParameterHandler(mappedStatement, parameterObject, boundSql);  
	    parameterHandler.setParameters(ps);  
	}  
	
//	 /** 
//	   * 复制MappedStatement对象 
//	   */  
//  private MappedStatement copyFromMappedStatement(MappedStatement ms,SqlSource newSqlSource) {  
//	    Builder builder = new Builder(ms.getConfiguration(),ms.getId(),newSqlSource,ms.getSqlCommandType());  
//	      
//	    builder.resource(ms.getResource());  
//	    builder.fetchSize(ms.getFetchSize());  
//	    builder.statementType(ms.getStatementType());  
//	    builder.keyGenerator(ms.getKeyGenerator());  
//	    builder.keyProperty(ms.getKeyProperties());  
//	    builder.timeout(ms.getTimeout());  
//	    builder.parameterMap(ms.getParameterMap());  
//	    builder.resultMaps(ms.getResultMaps());  
//	    builder.resultSetType(ms.getResultSetType());  
//	    builder.cache(ms.getCache());  
//	    builder.flushCacheRequired(ms.isFlushCacheRequired());  
//	    builder.useCache(ms.isUseCache());  
//	      
//	    return builder.build();  
//	  }  
        
}
